package simpledb.plan;

import simpledb.query.ProductScan;
import simpledb.query.Scan;
import simpledb.record.Schema;

/**
 * The Plan class corresponding to the <i>product</i>
 * relational algebra operator.
 *
 * @author Edward Sciore
 */
public class ProductPlan implements Plan {
    private Plan p1, p2;
    private Schema schema = new Schema();

    /**
     * Creates a new product node in the query tree,
     * having the two specified subqueries.
     *
     * @param p1 the left-hand subquery
     * @param p2 the right-hand subquery
     */
    public ProductPlan(Plan p1, Plan p2) {
        this.p1 = p1;
        this.p2 = p2;
        schema.addAll(p1.schema());
        schema.addAll(p2.schema());
    }

    /**
     * Creates a product scan for this query.
     *
     * @see simpledb.plan.Plan#open()
     */
    public Scan open() {
        Scan s1 = p1.open();
        Scan s2 = p2.open();
        return new ProductScan(s1, s2);
    }

    /**
     * Estimates the number of block accesses in the product.
     * The formula is:
     * <pre> B(product(p1,p2)) = B(p1) + R(p1)*B(p2) </pre>
     *
     * @see simpledb.plan.Plan#blocksAccessed()
     */
    public int blocksAccessed() {
        return p1.blocksAccessed() + (p1.recordsOutput() * p2.blocksAccessed());
    }

    /**
     * Estimates the number of output records in the product.
     * The formula is:
     * <pre> R(product(p1,p2)) = R(p1)*R(p2) </pre>
     *
     * @see simpledb.plan.Plan#recordsOutput()
     */
    public int recordsOutput() {
        return p1.recordsOutput() * p2.recordsOutput();
    }

    /**
     * Estimates the distinct number of field values in the product.
     * Since the product does not increase or decrease field values,
     * the estimate is the same as in the appropriate underlying query.
     *
     * @see simpledb.plan.Plan#distinctValues(java.lang.String)
     */
    public int distinctValues(String fldname) {
        if (p1.schema().hasField(fldname))
            return p1.distinctValues(fldname);
        else
            return p2.distinctValues(fldname);
    }

    /**
     * Returns the schema of the product,
     * which is the union of the schemas of the underlying queries.
     *
     * @see simpledb.plan.Plan#schema()
     */
    public Schema schema() {
        return schema;
    }
}
